﻿/** 
 * Twi Framework Core
 * @Author：feitianxinhong http://xuzhihong1987.blog.163.com/  
 * @Version：2.1.1
 * @CreateDate：2015-02-05
 * @PublicDate：
 * @Require: 
 * @Example: Visit http://blog.163.com/twi_fly  
 * @Copyright ©2010-2015 Tyingsoft 踏影软件开发工作室 之 Twilight/Twifly开源系列. 转载请保留本行注明.   
 * @License：Twi Framework of Tyingsoft is licensed under the MIT license.  
 * @Description：
 * ------------------------------------------------------------------
 *  编号    版本号    作者              修改日期        修改内容
 * ------------------------------------------------------------------
 *   1      1.0.0     feitianxinhong    2014-04-20      翻译部分之前基于Extjs的Twilight-4.0.0.js,基于jquery版本重新从1.0.0开始
 *   2      1.0.1     feitianxinhong    2014-04-21      集成重新翻译的Twi.Msg
 *   3      2.0.0     feitianxinhong    2014-06-02      Bootstrap响应式布局 
 *   4      2.1.0     feitianxinhong    2015-01-14      修改兼容之前的Twi.Msg、Twi.Apply，修正了Twi.Cookie等问题 
 *   5      2.1.1     feitianxinhong    2015-02-05      加入了Twi主题
 * ------------------------------------------------------------------
 */

(function ($) {
	var global = this,
           objectPrototype = Object.prototype,
           toString = objectPrototype.toString,
           enumerables = true,
           enumerablesTest = { toString: 1 },
           i;

	if (typeof Twi === 'undefined') {
		global.Twi = {};
	}
	Twi.global = global;

	for (i in enumerablesTest) {
		enumerables = null;
	}

	if (enumerables) {
		enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];
	}

	/**
    * An array containing extra enumerables for old browsers
    * @property {String[]}
    */
	Twi.enumerables = enumerables;

	/**
    * Copies all the properties of config to the specified object.
    * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
    * {@link Twi.Object#merge} instead.
    * @param {Object} object The receiver of the properties
    * @param {Object} config The source of the properties
    * @param {Object} defaults A different object that will also be applied for default values
    * @return {Object} returns obj
    */
	Twi.apply = function (object, config, defaults) {
		if (defaults) {
			Twi.apply(object, defaults);
		}

		if (object && config && typeof config === 'object') {
			var i, j, k;

			for (i in config) {
				object[i] = config[i];
			}

			if (enumerables) {
				for (j = enumerables.length; j--;) {
					k = enumerables[j];
					if (config.hasOwnProperty(k)) {
						object[k] = config[k];
					}
				}
			}
		}

		return object;
	};

	Twi.buildSettings = Twi.apply({
		baseCSSPrefix: 'x-',
		scopeResetCSS: false
	}, Twi.buildSettings || {});

	Twi.apply(Twi, {
		/**
        * A reusable empty function
        */
		emptyFn: function () { },

		baseCSSPrefix: Twi.buildSettings.baseCSSPrefix,

		/**
        * Copies all the properties of config to object if they don't already exist.
        * @param {Object} object The receiver of the properties
        * @param {Object} config The source of the properties
        * @return {Object} returns obj
        */
		applyIf: function (object, config) {
			var property;

			if (object) {
				for (property in config) {
					if (object[property] === undefined) {
						object[property] = config[property];
					}
				}
			}

			return object;
		},

		/**
        * Iterates either an array or an object. This method delegates to
        * {@link Twi.Array#each Twi.Array.each} if the given value is iterable, and {@link Twi.Object#each Twi.Object.each} otherwise.
        *
        * @param {Object/Array} object The object or array to be iterated.
        * @param {Function} fn The function to be called for each iteration. See and {@link Twi.Array#each Twi.Array.each} and
        * {@link Twi.Object#each Twi.Object.each} for detailed lists of arguments passed to this function depending on the given object
        * type that is being iterated.
        * @param {Object} scope (Optional) The scope (`this` reference) in which the specified function is executed.
        * Defaults to the object being iterated itself.
        * @markdown
        */
		iterate: function (object, fn, scope) {
			if (Twi.isEmpty(object)) {
				return;
			}

			if (scope === undefined) {
				scope = object;
			}

			if (Twi.isIterable(object)) {
				Twi.Array.each.call(Twi.Array, object, fn, scope);
			}
			else {
				Twi.Object.each.call(Twi.Object, object, fn, scope);
			}
		}
	});

	Twi.apply(Twi, {

		/**
        * This method deprecated. Use {@link Twi#define Twi.define} instead.
        * @method
        * @param {Function} superclass
        * @param {Object} overrides
        * @return {Function} The subclass constructor from the <tt>overrides</tt> parameter, or a generated one if not provided.
        * @deprecated 4.0.0 Use {@link Twi#define Twi.define} instead
        */
		extend: function () {
			// inline overrides
			var objectConstructor = objectPrototype.constructor,
                inlineOverrides = function (o) {
                	for (var m in o) {
                		if (!o.hasOwnProperty(m)) {
                			continue;
                		}
                		this[m] = o[m];
                	}
                };

			return function (subclass, superclass, overrides) {
				// First we check if the user passed in just the superClass with overrides
				if (Twi.isObject(superclass)) {
					overrides = superclass;
					superclass = subclass;
					subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function () {
						superclass.apply(this, arguments);
					};
				}

				//<debug>
				if (!superclass) {
					Twi.Error.raise({
						sourceClass: 'Twi',
						sourceMethod: 'extend',
						msg: 'Attempting to extend from a class which has not been loaded on the page.'
					});
				}
				//</debug>

				// We create a new temporary class
				var F = function () { },
                    subclassProto, superclassProto = superclass.prototype;

				F.prototype = superclassProto;
				subclassProto = subclass.prototype = new F();
				subclassProto.constructor = subclass;
				subclass.superclass = superclassProto;

				if (superclassProto.constructor === objectConstructor) {
					superclassProto.constructor = superclass;
				}

				subclass.override = function (overrides) {
					Twi.override(subclass, overrides);
				};

				subclassProto.override = inlineOverrides;
				subclassProto.proto = subclassProto;

				subclass.override(overrides);
				subclass.extend = function (o) {
					return Twi.extend(subclass, o);
				};

				return subclass;
			};
		}(),

		/**
        * Proxy to {@link Twi.Base#override}. Please refer {@link Twi.Base#override} for further details.

        Twi.define('My.cool.Class', {
        sayHi: function() {
        alert('Hi!');
        }
        }

        Twi.override(My.cool.Class, {
        sayHi: function() {
        alert('About to say...');

        this.callOverridden();
        }
        });

        var cool = new My.cool.Class();
        cool.sayHi(); // alerts 'About to say...'
        // alerts 'Hi!'

        * Please note that `this.callOverridden()` only works if the class was previously
        * created with {@link Twi#define)
        *
        * @param {Object} cls The class to override
        * @param {Object} overrides The list of functions to add to origClass. This should be specified as an object literal
        * containing one or more methods.
        * @method override
        * @markdown
        */
		override: function (cls, overrides) {
			if (cls.prototype.$className) {
				return cls.override(overrides);
			}
			else {
				Twi.apply(cls.prototype, overrides);
			}
		}
	});

	// A full set of static methods to do type checking
	Twi.apply(Twi, {

		/**
        * Returns the given value itself if it's not empty, as described in {@link Twi#isEmpty}; returns the default
        * value (second argument) otherwise.
        *
        * @param {Object} value The value to test
        * @param {Object} defaultValue The value to return if the original value is empty
        * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
        * @return {Object} value, if non-empty, else defaultValue
        */
		valueFrom: function (value, defaultValue, allowBlank) {
			return Twi.isEmpty(value, allowBlank) ? defaultValue : value;
		},

		/**
        * Returns the type of the given variable in string format. List of possible values are:
        *
        * - `undefined`: If the given value is `undefined`
        * - `null`: If the given value is `null`
        * - `string`: If the given value is a string
        * - `number`: If the given value is a number
        * - `boolean`: If the given value is a boolean value
        * - `date`: If the given value is a `Date` object
        * - `function`: If the given value is a function reference
        * - `object`: If the given value is an object
        * - `array`: If the given value is an array
        * - `regexp`: If the given value is a regular expression
        * - `element`: If the given value is a DOM Element
        * - `textnode`: If the given value is a DOM text node and contains something other than whitespace
        * - `whitespace`: If the given value is a DOM text node and contains only whitespace
        *
        * @param {Object} value
        * @return {String}
        * @markdown
        */
		typeOf: function (value) {
			if (value === null) {
				return 'null';
			}

			var type = typeof value;

			if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
				return type;
			}

			var typeToString = toString.call(value);

			switch (typeToString) {
				case '[object Array]':
					return 'array';
				case '[object Date]':
					return 'date';
				case '[object Boolean]':
					return 'boolean';
				case '[object Number]':
					return 'number';
				case '[object RegExp]':
					return 'regexp';
			}

			if (type === 'function') {
				return 'function';
			}

			if (type === 'object') {
				if (value.nodeType !== undefined) {
					if (value.nodeType === 3) {
						return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
					}
					else {
						return 'element';
					}
				}

				return 'object';
			}

			//<debug error>
			Twi.Error.raise({
				sourceClass: 'Twi',
				sourceMethod: 'typeOf',
				msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
			});
			//</debug>
		},

		/**
        * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if it is either:
        *
        * - `null`
        * - `undefined`
        * - a zero-length array
        * - a zero-length string (Unless the `allowEmptyString` parameter is set to `true`)
        *
        * @param {Object} value The value to test
        * @param {Boolean} allowEmptyString (optional) true to allow empty strings (defaults to false)
        * @return {Boolean}
        * @markdown
        */
		isEmpty: function (value, allowEmptyString) {
			return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Twi.isArray(value) && value.length === 0);
		},

		/**
        * Returns true if the passed value is a JavaScript Array, false otherwise.
        *
        * @param {Object} target The target to test
        * @return {Boolean}
        * @method
        */
		isArray: ('isArray' in Array) ? Array.isArray : function (value) {
			return toString.call(value) === '[object Array]';
		},

		/**
        * Returns true if the passed value is a JavaScript Date object, false otherwise.
        * @param {Object} object The object to test
        * @return {Boolean}
        */
		isDate: function (value) {
			return toString.call(value) === '[object Date]';
		},

		/**
        * Returns true if the passed value is a JavaScript Object, false otherwise.
        * @param {Object} value The value to test
        * @return {Boolean}
        * @method
        */
		isObject: (toString.call(null) === '[object Object]') ?
        function (value) {
        	// check ownerDocument here as well to exclude DOM nodes
        	return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } :
        function (value) {
        	return toString.call(value) === '[object Object]';
        },

		/**
        * Returns true if the passed value is a JavaScript 'primitive', a string, number or boolean.
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isPrimitive: function (value) {
			var type = typeof value;

			return type === 'string' || type === 'number' || type === 'boolean';
		},

		/**
        * Returns true if the passed value is a JavaScript Function, false otherwise.
        * @param {Object} value The value to test
        * @return {Boolean}
        * @method
        */
		isFunction:
        // Safari 3.x and 4.x returns 'function' for typeof <NodeList>, hence we need to fall back to using
        // Object.prorotype.toString (slower)
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function (value) {
        	return toString.call(value) === '[object Function]';
        } : function (value) {
        	return typeof value === 'function';
        },

		/**
        * Returns true if the passed value is a number. Returns false for non-finite numbers.
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isNumber: function (value) {
			return typeof value === 'number' && isFinite(value);
		},

		/**
        * Validates that a value is numeric.
        * @param {Object} value Examples: 1, '1', '2.34'
        * @return {Boolean} True if numeric, false otherwise
        */
		isNumeric: function (value) {
			return !isNaN(parseFloat(value)) && isFinite(value);
		},

		/**
        * Returns true if the passed value is a string.
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isString: function (value) {
			return typeof value === 'string';
		},

		/**
        * Returns true if the passed value is a boolean.
        *
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isBoolean: function (value) {
			return typeof value === 'boolean';
		},

		/**
        * Returns true if the passed value is an HTMLElement
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isElement: function (value) {
			return value ? value.nodeType === 1 : false;
		},

		/**
        * Returns true if the passed value is a TextNode
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isTextNode: function (value) {
			return value ? value.nodeName === "#text" : false;
		},

		/**
        * Returns true if the passed value is defined.
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isDefined: function (value) {
			return typeof value !== 'undefined';
		},

		/**
        * Returns true if the passed value is iterable, false otherwise
        * @param {Object} value The value to test
        * @return {Boolean}
        */
		isIterable: function (value) {
			return (value && typeof value !== 'string') ? value.length !== undefined : false;
		}
	});

	Twi.apply(Twi, {

		/**
        * Clone almost any type of variable including array, object, DOM nodes and Date without keeping the old reference
        * @param {Object} item The variable to clone
        * @return {Object} clone
        */
		clone: function (item) {
			if (item === null || item === undefined) {
				return item;
			}

			// DOM nodes
			// TODO proxy this to Twi.Element.clone to handle automatic id attribute changing
			// recursively
			if (item.nodeType && item.cloneNode) {
				return item.cloneNode(true);
			}

			var type = toString.call(item);

			// Date
			if (type === '[object Date]') {
				return new Date(item.getTime());
			}

			var i, j, k, clone, key;

			// Array
			if (type === '[object Array]') {
				i = item.length;

				clone = [];

				while (i--) {
					clone[i] = Twi.clone(item[i]);
				}
			}
				// Object
			else if (type === '[object Object]' && item.constructor === Object) {
				clone = {};

				for (key in item) {
					clone[key] = Twi.clone(item[key]);
				}

				if (enumerables) {
					for (j = enumerables.length; j--;) {
						k = enumerables[j];
						clone[k] = item[k];
					}
				}
			}

			return clone || item;
		},

		/**
        * @private
        * Generate a unique reference of Twi in the global scope, useful for sandboxing
        */
		getUniqueGlobalNamespace: function () {
			var uniqueGlobalNamespace = this.uniqueGlobalNamespace;

			if (uniqueGlobalNamespace === undefined) {
				var i = 0;

				do {
					uniqueGlobalNamespace = 'ExtBox' + (++i);
				} while (Twi.global[uniqueGlobalNamespace] !== undefined);

				Twi.global[uniqueGlobalNamespace] = Twi;
				this.uniqueGlobalNamespace = uniqueGlobalNamespace;
			}

			return uniqueGlobalNamespace;
		},

		/**
        * @private
        */
		functionFactory: function () {
			var args = Array.prototype.slice.call(arguments);

			if (args.length > 0) {
				args[args.length - 1] = 'var Twi=window.' + this.getUniqueGlobalNamespace() + ';' +
                    args[args.length - 1];
			}

			return Function.prototype.constructor.apply(Function.prototype, args);
		}
	});

	/**
    * Old alias to {@link Twi#typeOf}
    * @deprecated 4.0.0 Use {@link Twi#typeOf} instead
    * @method
    * @alias Twi#typeOf
    */
	Twi.type = Twi.typeOf;

	/*加载Twi.Msg*/
	if (!Twi.Msg) {
		if ($('body').length == 0) {
			document.write('<script src="/Framework/Twi.Msg-1.0.0.js" type="text/javascript"></script>');
		}
		else {
			$('body').append('<script src="/Framework/Twi.Msg-1.0.0.js" type="text/javascript"></script>');
		}
	}

	/**
     * 空函数
     */
	Twi.emptyFn = function () { }

	/**
	* PageHandler路径
	*/
	Twi.Url = "/PageHandler.ashx";

	/**
	*config:$.ajax(config)的参数
	*config.data:参数配置
	*config.success:（做了些改进）  twiReturn.success == true时执行的函数
	*/
	Twi.Ajax = function (config) {
		var ajaxConfig = $.extend({
			url: Twi.Url,
			dataType: 'JSON',
			type: 'POST',
			loadingMask: false,          //提交时是否自动加载Twi.LoadingMask...
			maskMsg: '正在处理请稍后...',
			errorMsgTarget: 'body',  //错误信息显示的位置，默认：body 。 ID需带井号，如：#id
			emptyMsg: false           //显示错误信息前是否清空内容
		}, config);

		/*TwiLoadingMask*/
		var twiMask;
		if (ajaxConfig.loadingMask) {
			twiMask = new Twi.LoadingMask({
				msg: ajaxConfig.maskMsg
			});
			twiMask.show();
		}
		delete ajaxConfig.loadMask;
		delete ajaxConfig.maskMsg;

		/*ajax请求成功方法*/
		ajaxConfig.success = function (twiReturn, textStatus, jqXHR) {
			if (twiReturn.success) {
				if (typeof config.success == "function") config.success(twiReturn);
			}
			else {
				if (Twi.Msg) {
					Twi.Msg.Error(twiReturn.message);
				}
				else {
				    if (ajaxConfig.emptyMsg) { $(ajaxConfig.errorMsgTarget).emptyMsg(); }                    
					$(ajaxConfig.errorMsgTarget).append('<div class="alert alert-warning alert-dismissable"><button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button><strong>温馨提示：</strong>' + twiReturn.errors + '</div>');
				}
				if (twiReturn.knowError == "102") {
					if (typeof Twi.QuickReLogin == 'function') {
						//快速重登录
						Twi.QuickReLogin();
					}
					else {
						location.href = "/Login/Index";
					}
				}
			}
		}

		/*ajax请求最后执行的方法*/
		ajaxConfig.complete = function () {
			if (typeof config.complete == "function") config.complete();
			if (twiMask) { twiMask.hide(); }
		}

		$.ajax(ajaxConfig);
	}

	Twi.QuickReLogin = function () {
		//QRL: QuickReLogin
		if ($('#qrlContainer').length == 0) {
			$("body").append('<div id="qrlContainer"></div>');
			$("#qrlContainer").load('/Login/QuickReLogin');
		}
	};

	/**
	* Grid一页显示的记录条数
	*/
	Twi.PageSize = 15;

	/**
	*开发人员才能维护基础数据，发布的时候，或是给测试的时候，这个应设为false
	*/
	Twi.IsDeveloper = false;
	Twi.NotDeveloperError = "系统基础支撑数据，只能由开发人员维护，</br>否则系误操作可能会引起系统对应功能无法操作！";
	/**
	*获取地址栏参数
	*/
	Twi.GetUrlParam = function (name) {
		var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
		var r = window.location.search.substr(1).match(reg);
		if (r != null) return unescape(r[2]);
		return null;
	}


	/**
	 *Cookies操作
	 */
	Twi.Cookies = {};
	Twi.Cookies.set = function (name, value) { var argv = arguments; var argc = arguments.length; var expires = (argc > 2) ? argv[2] : null; var path = (argc > 3) ? argv[3] : '/'; var domain = (argc > 4) ? argv[4] : null; var secure = (argc > 5) ? argv[5] : false; document.cookie = name + "=" + escape(value) + ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) + ((path == null) ? "" : ("; path=" + path)) + ((domain == null) ? "" : ("; domain=" + domain)) + ((secure == true) ? "; secure" : "") };
	Twi.Cookies.get = function (name) { var arg = name + "="; var alen = arg.length; var clen = document.cookie.length; var i = 0; var j = 0; while (i < clen) { j = i + alen; if (document.cookie.substring(i, j) == arg) return Twi.Cookies.getCookieVal(j); i = document.cookie.indexOf(" ", i) + 1; if (i == 0) break } return null };
	Twi.Cookies.clear = function (name) { if (Twi.Cookies.get(name)) { document.cookie = name + "=; expires=Thu, 01-Jan-70 00:00:01 GMT" } };
	Twi.Cookies.getCookieVal = function (offset) { var endstr = document.cookie.indexOf(";", offset); if (endstr == -1) { endstr = document.cookie.length } return unescape(document.cookie.substring(offset, endstr)) };

	/*当前登录用户*/
	Twi.CurrentUser = {
		FID: Twi.Cookies.get('Twi_User_FID'),
		FREALNAME: Twi.Cookies.get('Twi_User_FREALNAME'),
		FNAME: Twi.Cookies.get('Twi_User_FNAME'),
		PHONENUMBER: Twi.Cookies.get('Twi_User_PHONENUMBER'),
		PARKCODE: Twi.Cookies.get('Twi_User_PARKCODE'),
		STORAGECODE: Twi.Cookies.get('Twi_User_STORAGECODE')
	};

	Twi.ClearUserCookies = function () {
		Twi.Cookies.clear("Twi_User_FID");
		Twi.Cookies.clear("Twi_User_FREALNAME");
		// Cookies.clear("Twi_User_FNAME");
		Twi.CurrentUser.FID = "";
		Twi.CurrentUser.FREALNAME = "";
		Twi.CurrentUser.FNAME = "";
		Twi.CurrentUser.PHONENUMBER = "";
		Twi.CurrentUser.PARKCODE = "";
		Twi.CurrentUser.STORAGECODE = "";
	}

	/**
	 * 初始化方法
	 */
	$(function () {
		if (typeof twiSelf != "undefined") {
			if (twiVars && twiVars.autoInit == false) {
				//twiVars.autoInit = false  取消自动调用twiSelf.init();
			}
			else {
				if (typeof twiSelf.init == "function") twiSelf.init();
			}
		}
	});

	/**
	 * Twi.Skin:概念主题
	 */

	Twi.Skin = {
		turquoise: {
			skinColor: '#1ABC9C',
			skinColor2: '#16A085',
			skinName: '绿松石',
			skinCode: 'turquoise',
			skinIndex: 0
		},
		sunorange: {
			skinColor: '#F1C40F',
			skinColor2: '#F39C12',
			skinName: '向日橙',
			skinCode: 'sunorange',
			skinIndex: 1
		},
		dreamblue: {
			skinColor: '#3498DB',
			skinColor2: '#2980B9',
			skinName: '梦之蓝',
			skinCode: 'dreamblue',
			skinIndex: 2
		},
		amethyst: {
			skinColor: '#9B59B6',
			skinColor2: '#8E44AD',
			skinName: '紫水晶',
			skinCode: 'amethyst',
			skinIndex: 3
		},
		emerland: {
			skinColor: '#2ECC71',
			skinColor2: '#27AE60',
			skinName: '祖母绿',
			skinCode: 'emerland',
			skinIndex: 4
		},
		rainright: {
			skinColor: '#34495E',
			skinColor2: '#2C3E50',
			skinName: '黑雨夜',
			skinCode: 'rainright',
			skinIndex: 5
		},
		alizarin: {
			skinColor: '#E74C3C',
			skinColor2: '#C0392B',
			skinName: '茜素红',
			skinCode: 'alizarin',
			skinIndex: 6
		},
		brown: {
			skinColor: '#E67E22',
			skinColor2: '#D35400',
			skinName: '古韵棕',
			skinCode: 'brown',
			skinIndex: 7
		}
	};
})(window.jQuery);


